import HexUtils from '@/utils/HexUtils'
import Base64Utils from '@/utils/Base64Utils'

export default class AES {
  private key: CryptoKey | null = null
  private iv: ArrayBuffer | null = null
  private counter: ArrayBuffer | null = null
  private initialized: boolean = false
  private readonly mode: AesMode
  private keyBase64?: string
  private ivBase64?: string
  private counterBase64?: string

  /**
   * 私有构造函数，防止外部直接实例化
   * @param keyBase64 - Base64 格式的密钥
   * @param ivBase64 - Base64 格式的初始向量
   * @param mode - AES 模式（默认为 GCM）
   */
  private constructor(keyBase64?: string, ivBase64?: string, mode: AesMode = 'GCM') {
    this.keyBase64 = keyBase64
    this.ivBase64 = ivBase64
    this.mode = mode
  }

  /**
   * 静态方法，用于创建并初始化 AES 实例
   * @param keyBase64 - Base64 格式的密钥
   * @param ivBase64 - Base64 格式的初始向量
   * @param mode - AES 模式（默认为 GCM）
   * @returns 初始化后的 AES 实例
   */
  public static async initAES(keyBase64?: string, ivBase64?: string, mode: AesMode = 'GCM'): Promise<AES> {
    const aes = new AES(keyBase64, ivBase64, mode)
    await aes.initialize()
    return aes
  }

  /**
   * 异步初始化密钥和初始向量
   */
  private async initialize(): Promise<void> {
    if (this.initialized) return
    try {
      if (this.keyBase64) {
        await this._importKey(this.keyBase64)
      } else {
        await this.generateKey()
      }
      if (this.mode === 'CBC' && this.ivBase64) {
        await this._importIV(this.ivBase64)
      } else if (this.mode === 'GCM' && this.ivBase64) {
        // GCM 模式下 IV 是必须的
        await this._importIV(this.ivBase64)
      } else if (this.mode === 'CTR' && this.ivBase64) {
        await this._importIV(this.ivBase64)
      } else if (this.mode === 'GCM' && !this.ivBase64) {
        return Promise.reject('GCM 模式下必须提供初始向量')
      } else if (this.mode === 'CTR' && !this.ivBase64) {
        return Promise.reject('CTR 模式下必须提供初始向量')
      }
      if (this.mode === 'CTR') {
        if (this.iv && this.iv.byteLength < 16) {
          return Promise.reject('IV长度不够')
        }
        this.counter = this.iv ? this.iv.slice(0, 16) : crypto.getRandomValues(new Uint8Array(16)).buffer
      }
      this.initialized = true
    } catch (error) {
      console.error('初始化密钥失败:', error)
      throw error
    }
  }

  /**
   * 从 Base64 格式的字符串导入密钥
   * @param base64Key - Base64 格式的密钥
   */
  private async _importKey(base64Key: string): Promise<void> {
    const keyBuffer = Base64Utils.base64ToBuffer(base64Key)
    try {
      this.key = await crypto.subtle.importKey(
        'raw',
        keyBuffer,
        { name: `AES-${this.mode}`, length: 256 },
        false,
        ['encrypt', 'decrypt']
      )
    } catch (error) {
      console.error('导入密钥失败:', error)
      throw error
    }
  }

  /**
   * 从 Base64 格式的字符串导入初始向量
   * @param base64IV - Base64 格式的初始向量
   */
  private async _importIV(base64IV: string): Promise<void> {
    this.iv = Base64Utils.base64ToBuffer(base64IV)
  }

  /**
   * 生成密钥和初始向量
   */
  private async generateKey(): Promise<void> {
    try {
      this.key = await crypto.subtle.generateKey(
        { name: `AES-${this.mode}`, length: 256 },
        true,
        ['encrypt', 'decrypt']
      )

      if (this.mode === 'CBC') {
        this.iv = crypto.getRandomValues(new Uint8Array(16)).buffer
      } else if (this.mode === 'GCM') {
        this.iv = crypto.getRandomValues(new Uint8Array(12)).buffer
      } else if (this.mode === 'CTR') {
        this.iv = crypto.getRandomValues(new Uint8Array(16)).buffer
        this.counter = this.iv.slice(0, 16)
      }

      this.keyBase64 = Base64Utils.bufferToBase64(await crypto.subtle.exportKey('raw', this.key))
      if (this.iv) {
        this.ivBase64 = Base64Utils.bufferToBase64(this.iv)
      }
      if (this.counter) {
        this.counterBase64 = Base64Utils.bufferToBase64(this.counter)
      }
    } catch (error) {
      console.error('生成密钥失败:', error)
      throw error
    }
  }

  /**
   * 导出密钥为 Base64 格式的字符串
   * @returns Base64 格式的密钥
   */
  exportKey(): string {
    if (!this.keyBase64) {
      throw new Error('密钥未设置')
    }
    return this.keyBase64
  }

  /**
   * 导出初始向量为 Base64 格式的字符串
   * @returns Base64 格式的初始向量
   */
  exportIV(): string {
    if (!this.ivBase64) {
      throw new Error('初始向量未设置')
    }
    return this.ivBase64
  }

  /**
   * 导出计数器为 Base64 格式的字符串
   * @returns Base64 格式的计数器
   */
  exportCounter(): string {
    if (!this.counterBase64) {
      throw new Error('计数器未设置')
    }
    return this.counterBase64
  }

  /**
   * 加密数据
   * @param data - 要加密的数据
   * @returns 加密后的 Hex 格式字符串
   */
  async encryptData(data: string): Promise<string> {
    await this.initialize()
    if (!this.key || !this.iv) {
      throw new Error('密钥或初始向量未设置')
    }

    try {
      const encoder = new TextEncoder()
      const dataBuffer = encoder.encode(data)
      let encryptedBuffer: ArrayBuffer

      if (this.mode === 'CTR') {
        const ctrParams: AesCtrParams = {
          name: 'AES-CTR',
          counter: this.counter!,
          length: 128
        }
        encryptedBuffer = await crypto.subtle.encrypt(ctrParams, this.key, dataBuffer)
      } else {
        encryptedBuffer = await crypto.subtle.encrypt(
          {
            name: `AES-${this.mode}`,
            iv: this.iv
          },
          this.key,
          dataBuffer
        )
      }

      return HexUtils.bufferToHex(encryptedBuffer)
    } catch (error) {
      console.error('加密数据失败:', error)
      throw error
    }
  }

  /**
   * 解密数据
   * @param encryptedHex - 要解密的 Hex 格式字符串
   * @returns 解密后的字符串
   */
  async decryptData(encryptedHex: string): Promise<string> {
    await this.initialize()
    if (!this.key || !this.iv) {
      throw new Error('密钥或初始向量未设置')
    }

    try {
      const encryptedBuffer = HexUtils.hexToBuffer(encryptedHex)
      let decryptedBuffer: ArrayBuffer

      if (this.mode === 'CTR') {
        const ctrParams: AesCtrParams = {
          name: 'AES-CTR',
          counter: this.counter!,
          length: 128
        }
        decryptedBuffer = await crypto.subtle.decrypt(ctrParams, this.key, encryptedBuffer)
      } else {
        decryptedBuffer = await crypto.subtle.decrypt(
          {
            name: `AES-${this.mode}`,
            iv: this.iv
          },
          this.key,
          encryptedBuffer
        )
      }

      const decoder = new TextDecoder()
      return decoder.decode(decryptedBuffer)
    } catch (error) {
      console.error('解密数据失败:', error)
      throw error
    }
  }
}

// AES 模式枚举
type AesMode = 'CBC' | 'GCM' | 'CTR';

// AES-CTR 参数接口
interface AesCtrParams {
  name: 'AES-CTR';
  counter: ArrayBuffer;
  length: number;
}
